package com.yyfax.account.service;

import com.google.common.base.Function;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import com.jaws.core.common.log.Log;
import com.yyfax.account.dao.AccountInfoDao;
import com.yyfax.account.domain.AccountInfo;
import com.yyfax.account.vo.BaseTradeReq;
import com.yyfax.account.vo.BatchSettleVo;
import com.yyfax.account.vo.BatchTradeReq;
import com.yyfax.account.vo.FrozenReq;
import com.yyfax.account.vo.TradeReq;
import com.yyfax.account.vo.UnFrozenReq;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

@Service
public class AccountTradeServiceImpl implements AccountTradeService {

	private Logger logger = LoggerFactory.getLogger(getClass());

	@Resource
	private TradeServiceImpl tradeService;

	@Resource
	private FrozenServiceImpl frozenService;

	@Resource
	private UnFrozenServiceImpl unFrozen;

	@Resource
	private AccountTradeFactory tradeFactory;

	@Resource
	private BatchOperationService batchOperationService;

	@Resource
	private AccountInfoDao accountInfoDao;

	@Value("${BATCH_HANDLE_ACCOUNT_SIZE}")
	private Integer BATCH_HANDLE_ACCOUNT_NUM;

	@Override
	public void trade(TradeReq req) throws Exception {
		tradeService.doTrade(req, false);
	}

	@Override
	public void frozen(FrozenReq req) throws Exception {
		frozenService.doTrade(req, false);
	}

	@Override
	public void unFrozen(UnFrozenReq req) throws Exception {
		unFrozen.doTrade(req, false);
	}

	@SuppressWarnings("unchecked")
	@Override
	public void batchTrade(@SuppressWarnings("rawtypes") BatchTradeReq req) throws Exception {
		final String OP = "AccountTradeServiceImpl.batchTrade";
		logger.info(Log.op(OP).msg("批量交易开始").toString());
		List<BaseTradeReq> tradeLs = req.getTradeLs();
		// 1 按照账户排序,防止数据库死锁
		Collections.sort(tradeLs, new Comparator<BaseTradeReq>() {
			@Override
			public int compare(BaseTradeReq object1, BaseTradeReq object2) {
				return ComparisonChain.start().compare(object1.getAccountNo(), object2.getAccountNo(), Ordering.natural().reverse()).result();
			}
		});

		// 2 校验批量数据
		batchCheck(tradeLs);

		// 3 批量处理交易
		batchOperationService.handleBatchOperation(tradeLs);

		logger.info(Log.op(OP).msg("批量交易结束").toString());
	}

	@SuppressWarnings("unchecked")
	@Override
	public BatchSettleVo batchSettle(@SuppressWarnings("rawtypes") BatchTradeReq req) throws Exception {
		final String OP = "AccountTradeServiceImpl.batchSettle";
		logger.info(Log.op(OP).msg("批量结算开始").toString());
		List<BaseTradeReq> tradeLs = req.getTradeLs();
		// 1 按照账户排序,防止数据库死锁
		Collections.sort(tradeLs, new Comparator<BaseTradeReq>() {
			@Override
			public int compare(BaseTradeReq object1, BaseTradeReq object2) {
				return ComparisonChain.start().compare(object1.getAccountNo(), object2.getAccountNo(), Ordering.natural().reverse()).result();
			}
		});

		// 2 校验批量数据
		batchCheck(tradeLs);

		// 3 按 accountNo 切分结算请求数据,将同一个账户下的请求放到一个集合中
		BatchSettleVo vo = new BatchSettleVo();
		List<String> errorAccountLs = Lists.newArrayList();
		ImmutableListMultimap<String, BaseTradeReq> accountNoReqMap = Multimaps.index(tradeLs, BaseTradeReqAccountNo.INSTANCE);
		for (String accountNo : accountNoReqMap.keySet()) {
			List<BaseTradeReq> accountReqSettleList = accountNoReqMap.get(accountNo);

			// 4 保证同一个accountNo下所有的请求操作事务一致
			try {
				batchOperationService.handleBatchOperation(accountReqSettleList);
			} catch (Exception e) {
				logger.error(Log.op(OP).kv("accountReq", accountReqSettleList).msg("批量执行账户结算失败，数据回滚").toString(), e);
				errorAccountLs.add(accountNo);
			}
		}

		// 5 返回VO
		vo.setErrorAccountList(errorAccountLs);
		if(CollectionUtils.isNotEmpty(errorAccountLs)){
			vo.setState("-1");
			vo.setMessage("有执行失败的结算，请检查请求数据");
		}else {
			vo.setState("0");
			vo.setMessage("批量结算全部执行成功");
		}

		logger.info(Log.op(OP).msg("批量结算结束").toString());
		return vo;
	}

	/**
	 * 批量校验交易数据
	 * 
	 * @author liangqm
	 * @date 2017-10-24 18:01:18
	 * @params tradeLs 交易集合
	 * @return
	 * @since v1.0.0
	 */
	private void batchCheck(List<BaseTradeReq> tradeLs) {

		// 冻结 交易 解冻 有重复userId,去重后userId,根据userId 查询用户
		List<String> userIdList = Lists.transform(tradeLs, UniqueTradeReqUserId.INSTANCE);
		List<String> uniqueUserIdList = Lists.newArrayList(Sets.newHashSet(userIdList));// 为了使用 Lists.partition 分页方法，将去重后的set 再转成 List

		// 根据userId 查询AccountInfo
		Map<String, AccountInfo> accountsMap = Maps.newHashMap();// key = userId
		if (uniqueUserIdList.size() > BATCH_HANDLE_ACCOUNT_NUM) {// 分批差查询
			List<List<String>> pageList = Lists.partition(userIdList, BATCH_HANDLE_ACCOUNT_NUM);
			for (List<String> idList : pageList) {
				accountsMap.putAll(this.obtainAccountsMap(idList));
			}
		} else {
			accountsMap = this.obtainAccountsMap(uniqueUserIdList);
		}

		for (BaseTradeReq tradeReq : tradeLs) {
			tradeFactory.getAdaptiveTradeService(tradeReq.getTradeType()).batchCheckBusiness(tradeReq, accountsMap.get(tradeReq.getUserId()));
		}
	}

	/**
	 * 通过userId 集合 批量获取用户
	 *
	 * @author liangqm
	 * @date 2017-10-26 17:18:18
	 * @param userIdList
	 * @return
	 * @since v1.0.0
	 */
	private Map<String, AccountInfo> obtainAccountsMap(List<String> userIdList) {
		List<AccountInfo> accountInfoList = accountInfoDao.batchQueryAccountInfoByUser(userIdList);
		Map<String, AccountInfo> accountMap = Maps.uniqueIndex(accountInfoList, UniqueAccountUserId.INSTANCE);
		return accountMap;
	}

	enum UniqueTradeReqUserId implements Function<BaseTradeReq, String> {
		INSTANCE;

		@Override
		public String apply(BaseTradeReq input) {
			return input.getUserId();
		}
	}

	enum UniqueAccountUserId implements Function<AccountInfo, String> {
		INSTANCE;

		@Override
		public String apply(AccountInfo input) {
			return input.getUserId();
		}
	}

	enum BaseTradeReqAccountNo implements Function<BaseTradeReq, String> {
		INSTANCE;

		@Override
		public String apply(BaseTradeReq req) {
			return req.getAccountNo();
		}
	}


	@Override
	public void batchComplexTrade(BatchTradeReq req) {
		final String op = "AccountTradeServiceImpl.batchComplexTrade";

		logger.info(Log.op(op).msg("批量综合交易开始").toString());


		// 1 按照账户排序,防止数据库死锁
		List<BaseTradeReq> tradeLs = req.getTradeLs();
		Collections.sort(tradeLs, new Comparator<BaseTradeReq>() {
			@Override
			public int compare(BaseTradeReq object1, BaseTradeReq object2) {
				return ComparisonChain.start().compare(object1.getAccountNo(), object2.getAccountNo(), Ordering.natural().reverse()).result();
			}
		});

		// 2 校验批量数据
		batchCheck(tradeLs);

		// 3 批量处理交易
		batchOperationService.handleBatchOperation(tradeLs);

		logger.info(Log.op(op).msg("批量综合交易结束").toString());
	}

}
